<?php

namespace app\kernel\crontab;

use app\kernel\crontab;
use app\kernel\Process;
use ReflectionFunction;
use think\Exception;
use think\facade\Config;
use think\facade\Log;
use Workerman\Crontab\Crontab as BaseCrontab;

/**
 *  定时器进程
 */
class CrontabProcess extends Process
{

    protected $name = 'crontab';

    protected $crontab = [];

    /**
     * @return  void;
     * @throws Exception
     * @throws \ReflectionException
     */
    protected function beforeWorkerStart(Process $process): void
    {

        $crontab = $this->app->config->get('crontab');
        if (empty($crontab)) {
            return;
        }

        /**
         * @var $item ['work'=>'21321321','rule'=>'1 * * * * *']
         */
        foreach ($crontab as $key=> $item) {
            $closure = $key instanceof \Closure ? $this->createClosureCrontab($key) : $this->createClassCrontab($item['work']);
            $this->crontab[] = [
                'work' => $closure,
                'rule' => $item['rule']
            ];
        }

    }

    /**
     * @param Process $process
     * @return mixed|void
     */
    protected function workerStart(Process $process): void
    {

        //注册日志
        $this->registerLogger();
        // 日志注册
        foreach ($this->crontab as $crontab) {
            new BaseCrontab($crontab['rule'], $crontab['work']);
        }
    }

    /**
     *
     * @param \Closure $work
     * @return \Closure
     * @throws \ReflectionException|Exception
     */
    private function createClosureCrontab(\Closure $work): \Closure
    {

        $method = new ReflectionFunction($work);
        if (!empty($method->getParameters())) {
            throw new Exception(" function have Parameter !");
        }

        return $method->getClosure();
    }

    /**
     *
     * @param $work
     * @return \Closure
     * @throws Exception|\ReflectionException
     */
    private function createClassCrontab($work): \Closure
    {

        if (strpos($work, "::")) {
            list($class, $method) = explode("::", $work);
        } else {
            $class = $work;
            $method = 'handle';
        }

        if (!class_exists($class)) {
            throw new Exception("class:{$class} not exists !");
        }

        $refClass = new \ReflectionClass($class);
        //查询是否存在构造函数
        if ($constructor = $refClass->getConstructor()) {
            // 查询构造函数是否有参数
            if (!empty($constructor->getParameters())) {
                throw new Exception('Constructor have Parameter !');
            }
        }

        // 方法是否存在
        if (!$refClass->hasMethod($method)) {
            throw new Exception("{$method}   not existent !");
        }

        $refMethod = $refClass->getMethod($method);
        if (!empty($refMethod->getParameters())) {
            throw new Exception('method have Parameter !');
        }

        if (!$refMethod->isPublic()) {
            throw new Exception('method is not  Public!');
        }
        $class = $refClass->newInstance();

        return function () use ($class, $refMethod) {
            return $refMethod->invoke($class);
        };
    }

    /**
     * 注册日志
     * @param $class
     * @return void
     */
    protected function registerLogger()
    {

        $config = Config::get('log.channels.file');
        $config['path'] = app()->getRuntimePath() . 'log/Crontab';
        $channels = array_merge(Config::get('log.channels'), ['crontab' => $config]);
        Config::set(['channels' => $channels], 'log');
        Config::set(['default' => 'crontab'], 'log');

    }


}